#include "facade_geography.h"
#include "facade_grammar.h"
#include "scale_model.h"

#include "pcl\common\centroid.h"
#include <pcl/common/common.h>
#include <pcl/search/kdtree.h>
#include <pcl\visualization\pcl_plotter.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/octree/octree.h>
#include <pcl\kdtree\kdtree_flann.h>

#include "boost/progress.hpp"
#include "boost/thread.hpp"

#include <fstream>
#include <sstream>
#include <limits>
#include <assert.h>

boost::shared_mutex mutex;

class CalculateFeatureJob {
public:
	CalculateFeatureJob(pcl::octree::OctreePointCloudSearch<pcl::PointXYZ>::Ptr otree, 
		FacadeGeography* facade, pcl::PointCloud<pcl::PointXYZ>::Ptr facade_cloud, 
		boost::progress_display* display,
		int width, int height_begin, int height_end, double z) {
		octree_ = otree;
		facade_ = facade;
		width_ = width;
		height_begin_ = height_begin;
		height_end_ = height_end;
		facade_cloud_ = facade_cloud;
		display_ = display;
		z_ = z;
	}

	void Run() {
		double resolution_ = facade_->get_resolution();
		cv::Point3d extents_min_ = facade_->get_extents_min();
		Eigen::Vector4f max_pt, min_pt;
		pcl::PointXYZ search_point;
		for (int i = 0; i < width_; ++i)
		{
			for ( int j = height_begin_; j < height_end_; ++j)
			{
				// define x, y coordinate for search point
				search_point.x = i * resolution_ + extents_min_.x + resolution_ / 2.0;
				search_point.y = j * resolution_ + extents_min_.y + resolution_ / 2.0;

				// two criteria
				int point_number = 0;
				double z_depth = 0.0;

				// point-search along z-direction...
				for (double z = (z_ - resolution_ * 10); z < (z_ + resolution_ * 10); z += (resolution_ * 2)) {
					// define z-coordinate for search point
					search_point.z = z;

					// search in the grid
					std::vector<int> indices;
					if (!octree_->voxelSearch(search_point, indices))
						continue;

					// get #points
					point_number += indices.size();
					
					// get z-depth
					if (indices.size() > 0) {	
						for (int n = 0; n < indices.size(); ++n) {
							z_depth += facade_cloud_->points[indices[n]].z;
						}
					}
				}
				
				facade_->SetProperty(i, j, 0, point_number);
				if (point_number != 0)
					facade_->SetProperty(i, j, 1, z_depth / point_number);
				else
					facade_->SetProperty(i, j, 1, 0);
	
				boost::unique_lock<boost::shared_mutex> lock(mutex);
				++(*display_);
				lock.unlock();
			}
		}
	}

private:
	FacadeGeography* facade_;
	int width_;
	int height_begin_, height_end_;
	double z_;
	pcl::PointCloud<pcl::PointXYZ>::Ptr facade_cloud_;
	pcl::octree::OctreePointCloudSearch<pcl::PointXYZ>::Ptr octree_;
	boost::progress_display* display_;
};

FacadeGeography::FacadeGeography(int widthX, int widthY, std::shared_ptr<FacadeGrammar> facade_grammar)
{
	init(widthX, widthY, facade_grammar);
}

FacadeGeography::~FacadeGeography()
{
	destroy();
}

FacadeGeography::FacadeGeography (std::shared_ptr<FacadeGrammar> facade_grammar)
{
	facade_grammar_ = facade_grammar;
	//	init(res, extMin, extRange);
}

void FacadeGeography::init ( int widthX, int widthY, std::shared_ptr<FacadeGrammar> facade_grammar)
{
	facade_grammar_ = facade_grammar;

	width_ = widthX;
	height_ = widthY;

	depth_ = facade_grammar_->get_terminal_symbol_size();
	grd_.resize (depth_);
	for ( int i = 0; i < depth_; ++i)
	{
		grd_[i] = new double*[height_];
		for ( int j = 0; j < height_; ++j)
		{
			grd_[i][j] = new double[width_];
			for (int k = 0; k < width_; ++k)
				grd_[i][j][k] = 0.0;
		}
	}
}

void FacadeGeography::init ( double res, const cv::Point2d& extMin, const cv::Point2d& extRange )
{
	//resolution_ = res;
	//extents_min_ = extMin;
	//extents_range_ = extRange;

	//width_ = (int)(extRange.x / res + 0.5);
	//height_ = (int)(extRange.y / res + 0.5);
	//convert_ratio_ = cv::Point2d((width_-1.0) / extRange.x, (height_-1.0) / extRange.y);

	//depth_ = facade_grammar_->get_terminal_symbol_size();
	//grd_.resize(depth_);
	//for ( int i = 0; i < depth_; ++i)
	//{
	//	//grd_[i] = new Mat(height_, width_, CV_8UC1);
	//	grd_[i] = new double*[height_];
	//	for ( int j = 0; j < height_; ++j)
	//	{
	//		grd_[i][j] = new double[width_];
	//		for (int k = 0; k < width_; ++k)
	//		{
	//			grd_[i][j][k] = 0.0;
	//		}	
	//	}
	//}
}

void FacadeGeography::LoadGridScale(const char* filename) {
	ifstream ifs(filename);
	std::string dummy;
	ifs >> dummy >> extents_min_.x;
	ifs >> dummy >> extents_min_.y;
	ifs >> dummy >> extents_min_.z;
	ifs >> dummy >> extents_range_.x;
	ifs >> dummy >> extents_range_.y;
	ifs >> dummy >> extents_range_.z;
	ifs.close();
}

void FacadeGeography::CreateGridFromPointCloudEnhance(pcl::PointCloud<pcl::PointXYZ>::Ptr facade_cloud, double resolution, int n_threads) {
	resolution_ = resolution;
	pcl::PointXYZ min_extent, max_extent;
	pcl::getMinMax3D(*facade_cloud, min_extent, max_extent);

	width_ = (int)(extents_range_.x / resolution_ + 0.5);
	height_ = (int)(extents_range_.y / resolution_ + 0.5);
	convert_ratio_ = cv::Point2d((width_-1.0) / extents_range_.x, (height_-1.0) / extents_range_.y);

	depth_ = 3 + facade_grammar_->get_terminal_symbol_size();

	grd_.resize(depth_);
	for ( int i = 0; i < depth_; ++i)
	{
		grd_[i] = new double*[height_];
		for ( int j = 0; j < height_; ++j)
		{
			grd_[i][j] = new double[width_];
			for (int k = 0; k < width_; ++k)
			{
				grd_[i][j][k] = 0.0;
			}	
		}
	}

	double votex_size = resolution_;// * 2;
	pcl::octree::OctreePointCloudSearch<pcl::PointXYZ>::Ptr octree(
		new pcl::octree::OctreePointCloudSearch<pcl::PointXYZ>(votex_size));
	octree->defineBoundingBox(extents_min_.x, extents_min_.y, min_extent.z, extents_min_.x + extents_range_.x,
		extents_min_.y + extents_range_.y, max_extent.z);
	octree->setInputCloud(facade_cloud);
	octree->addPointsFromInputCloud();

	boost::progress_display display(width_ * height_);

	std::vector<boost::thread> threads;
	threads.resize(n_threads);

	int jobs_per_thread = std::ceil((double)height_ / (double)n_threads);
	int height_begin = 0;
	int height_end = 0;
	for (int i = 0; i < n_threads; ++i) {
		height_end = height_begin + jobs_per_thread;
		if (height_end > height_)
			height_end = height_;

		std::shared_ptr<CalculateFeatureJob> job = std::shared_ptr<CalculateFeatureJob>(new CalculateFeatureJob(
			octree, this,facade_cloud,
			&display, width_, height_begin, height_end, (extents_min_.z + extents_range_.z / 2.0)));


		threads[i] = boost::thread(boost::bind(&CalculateFeatureJob::Run, job));

		height_begin += jobs_per_thread;
		if (height_begin >= height_)
			break;
	}

	for (int i = 0; i < n_threads; ++i) {
		threads[i].join();
	}

}
void FacadeGeography::NormalizeFeatures() {
	double max1 = std::numeric_limits<double>::min();
	double min1 = std::numeric_limits<double>::max();
	double max2 = std::numeric_limits<double>::min();
	double min2 = std::numeric_limits<double>::max();

	// first loop, find maximal and minimal
	for (int i = 0; i < width_; ++i) {
		for (int j = 0; j < height_; ++j) {
			max1 = max1 < GetProperty(i, j, 0) ? GetProperty(i, j, 0) : max1;
			max2 = max2 < GetProperty(i, j, 1) ? GetProperty(i, j, 1) : max2;

			min1 = min1 > GetProperty(i, j, 0) ? GetProperty(i, j, 0) : min1;
			min2 = min2 > GetProperty(i, j, 1) ? GetProperty(i, j, 1) : min2;
		}
	}

	// second loop, normalize data to [-1, 1]
	for (int i = 0; i < width_; ++i) {
		for (int j = 0; j < height_; ++j) {
			double normalized_value1, normalized_value2;
			normalized_value1 = ScaleModel::AB2MN(GetProperty(i, j, 0), min1, max1, -1, 1);
			normalized_value2 = ScaleModel::AB2MN(GetProperty(i, j, 1), min2, max2, -1, 1);

			SetProperty(i, j, 0, normalized_value1);
			SetProperty(i, j, 1, normalized_value2);
		}
	}
}

void FacadeGeography::CreateGridFromPointCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr facade_cloud, double resolution) {
	/*pcl::PointXYZ min_extent, max_extent;
	pcl::getMinMax3D(*facade_cloud, min_extent, max_extent);

	resolution_ = resolution;
	extents_min_.x = min_extent.x;
	extents_min_.y = min_extent.y;
	extents_range_.x = max_extent.x - min_extent.x;
	extents_range_.y = max_extent.y - min_extent.y;*/
	resolution_ = resolution;
	pcl::PointXYZ min_extent, max_extent;
	pcl::getMinMax3D(*facade_cloud, min_extent, max_extent);

	width_ = (int)(extents_range_.x / resolution_ + 0.5);
	height_ = (int)(extents_range_.y / resolution_ + 0.5);
	convert_ratio_ = cv::Point2d((width_-1.0) / extents_range_.x, (height_-1.0) / extents_range_.y);

	

	depth_ = 3;
	grd_.resize(depth_);
	for ( int i = 0; i < depth_; ++i)
	{
		grd_[i] = new double*[height_];
		for ( int j = 0; j < height_; ++j)
		{
			grd_[i][j] = new double[width_];
			for (int k = 0; k < width_; ++k)
			{
				grd_[i][j][k] = 0.0;
			}	
		}
	}

	boost::progress_display display(width_ * height_);

	Eigen::Vector4f max_pt, min_pt;
	for (int i = 0; i < width_; ++i)
	{
		for ( int j = 0; j < height_; ++j)
		{
			min_pt(0) = i * resolution_ + extents_min_.x;
			min_pt(1) = j * resolution_ + extents_min_.y;
			min_pt(2) = min_extent.z;
			min_pt(3) = 1.0;

			max_pt(0) = min_pt(0) + resolution_;
			max_pt(1) = min_pt(1) + resolution_;
			max_pt(2) = max_extent.z;
			max_pt(3) = 1.0;

			std::vector<int> indices;
			pcl::getPointsInBox(*facade_cloud, min_pt, max_pt, indices);

			// #points
			assert(indices.size() >= 0);
			SetProperty(i, j, 0, indices.size());

			double z_sum = 0;
			if (indices.size() > 0) {	
				for (int z = 0; z < indices.size(); ++z) {
					z_sum += facade_cloud->points[indices[z]].z;
				}
				z_sum /= indices.size();
			} else {
				z_sum = min_extent.z - 10;
			}

			// z-depth
			SetProperty(i, j, 1, z_sum);
#if 0
			if (indices.size() <= 1) // window
			{
				SetProperty(i, j, 1, 1);
			}
			else // wall
			{
				double height = 0;
				for (int k = 0; k < indices.size(); ++k)
				{
					height += facade_cloud->points[k].z;
				}
				height /= indices.size();

				if (height < -0.5)
				{
					SetProperty(i, j, 1, 1); // window
				}
				else if (height > 0.5)
				{
					SetProperty(i, j, 2, 1); // over wall
				}
				else
				{
					SetProperty(i, j, 0, 1); // wall
				}
			}
#endif
			indices.clear();
			++display;
		}
	}

}

void FacadeGeography::destroy()
{
	for ( int i = 0; i < depth_; ++i )
	{
		if ( grd_[i] != NULL )
		{
			for ( int j = 0; j < height_; ++j )
			{
				delete[] grd_[i][j];
				grd_[i][j] = NULL;

			}
			delete[] grd_[i];
		}
	}
	resolution_ = 0;
	width_ = 0;
	height_ = 0;
	depth_ = 0;
}

double FacadeGeography::GetProperty ( int width, int height, int depth)
{
	return grd_[depth][height][width];
}

double FacadeGeography::GetProperty ( const cv::Point2d& pos, int depth )
{
	cv::Point2d localPos = pos - cv::Point2d(extents_min_.x, extents_min_.y);
	int width = (int)(localPos.x * convert_ratio_.x);
	int height = (int)(localPos.y * convert_ratio_.y);

	return grd_[depth][height][width];
}

void FacadeGeography::SetProperty ( int width, int height, int depth, double value )
{
	// 	uchar* row = grd_[depth]->ptr<uchar>(height);
	// 	row[width] = (uchar)value;
	grd_[depth][height][width] = value;
}

void FacadeGeography::RenderGrd ( std::string path )
{
	for ( size_t depth = 0; depth < grd_.size(); ++depth)
	{
		cv::Mat mat(height_, width_, CV_8UC1);

		// pass 1: find out max and min
		double max = std::numeric_limits<double>::min();
		double min = std::numeric_limits<double>::max();

		for (int i = 0; i < height_; ++i) {
			for (int j = 0; j < width_; ++j) {
				double value = grd_[depth][i][j];
				if (max < value)
					max = value;
				if (min > value)
					min = value;
			}
		}
		double range = max - min;

		if (range == 0)
			continue;

		// pass 2: convert value to 0-255 scale
		for ( int i = 0; i < height_; ++i) {
			for ( int j = 0; j < width_; ++j) {
				uchar* row = mat.ptr<uchar>(i);
				// grd_[depth][i][j] = (grd_[depth][i][j] - min) / range; // normalize value to [0, 1]
				double scaled_value = (grd_[depth][i][j] - min) / range;
				row[j] = (uchar)(scaled_value * 255);
			}
		}

		std::ostringstream image_path;
		if (depth > 2)
			image_path << path << facade_grammar_->get_terminal_symbol(depth - 3) << ".png";
		else if (depth == 0) 
			image_path << path << "density" << ".png";
		else if (depth == 1)
			image_path << path << "depth" << ".png";
		else 
			image_path << path << "labels" << ".png";
		imwrite(image_path.str(), mat);
	}
}

#if 0
void FacadeGeography::DebugCalculateFeature(std::string filename) {

	cv::Mat mat(height_, width_, CV_8UC3);

	cv::Mat_<cv::Vec3b> _Mat = mat;

	for ( int i = 0; i < height_; ++i)
	{
		for ( int j = 0; j < width_; ++j)
		{
			_Mat(i, j)[0] = (uchar)(grd_[1][i][j] * 255);
			_Mat(i, j)[1] = (uchar)(grd_[1][i][j] * 255);
			_Mat(i, j)[2] = (uchar)(grd_[1][i][j] * 255);
		}
	}
	int thickness = 1;
	int line_type = 8;

	int sum = 0;
	for (int i = 0; i < action_parameters_vertical_.size(); ++i)
	{
		int length = action_parameters_vertical_[i].second + sum;
		sum = length;
		cv::Point start(0, length);
		cv::Point end(width_, length);

		if (action_parameters_vertical_[i].first == "wall")
		{
			cv::line(mat, start, end, cv::Scalar(255, 0, 0), thickness, line_type);
		}
		else
		{
			cv::line(mat, start, end, cv::Scalar(0, 0, 255), thickness, line_type);
		}

	}

	sum = 0;
	for (int i = 0; i < action_parameters_horizontal_.size(); ++i)
	{
		int length = action_parameters_horizontal_[i].second + sum;
		sum = length;
		cv::Point start(length, 0);
		cv::Point end(length, height_);

		if (action_parameters_horizontal_[i].first == "wall")
		{
			cv::line(mat, start, end, cv::Scalar(255, 0, 0), thickness, line_type);
		}
		else
		{
			cv::line(mat, start, end, cv::Scalar(0, 0, 255), thickness, line_type);
		}
	}

	imwrite(filename, mat);
}
#endif

void FacadeGeography::SaveGrids ( std::string& filename )
{
	// Save grid header.
	std::string grid_header = filename + "header.txt";
	std::ofstream ofs(grid_header.c_str());
	ofs << "Width " << width_ << std::endl;
	ofs << "Height " << height_ << std::endl;
	ofs << "Depth " << depth_ << std::endl;
	ofs << "Resolution " << resolution_ << std::endl;
	ofs << "Extent_Min " << extents_min_.x << " " << extents_min_.y << std::endl;
	ofs << "Extent_Range " << extents_range_.x << " " << extents_range_.y << std::endl;
	ofs << "Convert_Ratio " << convert_ratio_.x << " " << convert_ratio_.y << std::endl;
	ofs.close();

	// Save grids
	for ( size_t depth = 0; depth < grd_.size(); ++depth)
	{
		std::ostringstream oss;
		if (depth > 2)
			oss << filename << facade_grammar_->get_terminal_symbol(depth - 3) << ".txt";
		else if (depth == 0) 
			oss << filename << "density" << ".txt";
		else if (depth == 1)
			oss << filename << "depth" << ".txt";
		else 
			oss << filename << "labels" << ".txt";

		std::ofstream ofs(oss.str().c_str());

		for ( int i = 0; i < height_; ++i)
		{
			for ( int j = 0; j < width_; ++j)
			{
				ofs << grd_[depth][i][j] << " ";
			}
			ofs << std::endl;
		}
		ofs.close();
	}
}

void FacadeGeography::LoadGrids( std::string& filename )
{
	// load header first
	std::string header_path = filename + "header.txt";
	std::ifstream ifs(header_path.c_str());
	std::string dummy;
	ifs >> dummy >> width_;
	ifs >> dummy >> height_;
	ifs >> dummy >> depth_;
	ifs >> dummy >> resolution_;
	ifs >> dummy >> extents_min_.x >> extents_min_.y;
	ifs >> dummy >> extents_range_.x >> extents_range_.y;
	ifs >> dummy >> convert_ratio_.x >> convert_ratio_.y;
	ifs.close();

	grd_.resize(depth_);

	for (size_t depth = 0; depth < depth_; ++depth)
	{
		std::ostringstream oss;
		if (depth > 2)
			oss << filename << facade_grammar_->get_terminal_symbol(depth - 3) << ".txt";
		else if (depth == 0) 
			oss << filename << "density" << ".txt";
		else if (depth == 1)
			oss << filename << "depth" << ".txt";
		else 
			oss << filename << "labels" << ".txt";
		std::ifstream ifs(oss.str().c_str());
		//	std::string new_path = filename + facade_grammar_->get_terminal_symbol(depth) + ".txt";
		//	std::ifstream ifs(new_path.c_str() );

		grd_[depth] = new double*[height_];
		for ( int j = 0; j < height_; ++j)
		{
			grd_[depth][j] = new double[width_];
			for (int k = 0; k < width_; ++k)
			{
				grd_[depth][j][k] = 0.0;
			}	
		}

		for ( int i = 0; i < height_; ++i )
		{
			for ( int j = 0; j < width_; ++j )
			{
				ifs >> grd_[depth][i][j];
			}
		}

		ifs.close();
	}
}

void FacadeGeography::SetSymbol(double xmin, double xmax, double ymin, double ymax, int depth) {
	for (int x = xmin; x < xmax; ++x)
	{
		for (int y = ymin; y < ymax; ++y)
		{
			SetProperty(x, y, depth, 1.0);
		}	
	}
}

std::vector<double> FacadeGeography::Smooth(std::vector<double> feature, int left, int right) {
	std::vector<double> new_feature;
	new_feature.resize(feature.size());

	for (int i = 0; i < feature.size(); ++i) {
		int count = 1;
		double sum = feature[i];
		for (int l = 1; l <= left; ++l) {
			if (i - l >= 0) {
				sum += feature[i - l];
				++count;
			}
		}
		for (int r = 1; r <= right; ++r) {
			if (i + r < feature.size()) {
				sum += feature[i + r];
				++count;
			}
		}
		new_feature[i] = sum / count;
	}
	return new_feature;
}

std::vector<int> FacadeGeography::FindLoacalMinimalAndMaximal(std::vector<double>& feature) {
	std::vector<int> mins_maxs;
	for (int i = 1; i < feature.size() - 1; ++i) {
		if (feature[i] < feature[i - 1] && feature[i] < feature[i + 1])
		{
			mins_maxs.push_back(i);
		}
		if (feature[i] > feature[i - 1] && feature[i] > feature[i + 1]) {
			mins_maxs.push_back(i);
		}
	}
	return mins_maxs;
}

void FacadeGeography::ShowFeature(std::string path) {
	int symbols = this->facade_grammar_->get_terminal_symbol_size();
	vertical_features.resize(symbols);
	horizonal_features.resize(symbols);
	action_parameters_horizontal_.resize(symbols);
	action_parameters_vertical_.resize(symbols);

	std::vector<double> width, height;
	for (int i = 0; i < width_; ++i)
	{
		width.push_back(i);
	}
	for (int j = 0; j < height_; ++j)
	{
		height.push_back(j);
	}

	for (int s = 0; s < symbols; ++s) {
		//std::vector<double> vertical, horizonal;
		// vertical feature
		for (int i = 0; i < height_; ++i)
		{
			double sum = 0;
			for (int j = 0; j < width_; ++j)
			{
				sum += grd_[s + 3][i][j];
			}
			sum /= width_;
			vertical_features[s].push_back(sum);
		}

		// horizontal feature
		for (int i = 0; i < width_; ++i)
		{
			double sum = 0;
			for (int j = 0; j < height_; ++j)
			{
				sum += grd_[s + 3][j][i];
			}
			sum /= height_;
			horizonal_features[s].push_back(sum);
		}

		for (int i = 0; i < 5; ++i) {
		vertical_features[s] = Smooth(vertical_features[s], 5, 5);
		horizonal_features[s] = Smooth(horizonal_features[s], 5, 5);
		}

		for (int i = 0; i < height_ - 1; ++i) {
			vertical_features[s][i] = vertical_features[s][i + 1] - vertical_features[s][i];
		}
		vertical_features[s][height_ - 1] = 0.0;

		for (int i = 0; i < width_ - 1; ++i) {
			horizonal_features[s][i] = horizonal_features[s][i + 1] - horizonal_features[s][i];
		}
		horizonal_features[s][width_ - 1] = 0.0;
#if 0
		std::shared_ptr<pcl::visualization::PCLPlotter> plotter(new pcl::visualization::PCLPlotter);
		std::shared_ptr<pcl::visualization::PCLPlotter> plotter2(new pcl::visualization::PCLPlotter);
		plotter->addPlotData(width, horizonal_features[s], "Horizontal Feature", vtkChart::LINE);
		plotter2->addPlotData(vertical_features[s], height, "Vertical Feature", vtkChart::LINE);

		plotter->plot();
		plotter2->plot();
#endif
		cv::Mat mat(height_, width_, CV_8UC3);
		cv::Mat mat2(height_, width_, CV_8UC3);

		// pass 1: find out max and min
		double max = std::numeric_limits<double>::min();
		double min = std::numeric_limits<double>::max();

		for (int i = 0; i < height_; ++i) {
			for (int j = 0; j < width_; ++j) {
				double value = grd_[s + 3][i][j];
				if (max < value)
					max = value;
				if (min > value)
					min = value;
			}
		}
		double range = max - min;

		if (range == 0)
			continue;

		cv::Mat_<cv::Vec3b> _Mat = mat;
		cv::Mat_<cv::Vec3b> _Mat2 = mat2;

		// pass 2: convert value to 0-255 scale
		for ( int i = 0; i < height_; ++i) {
			for ( int j = 0; j < width_; ++j) {
				//uchar* row = mat.ptr<uchar>(i);
				double scaled_value = (grd_[s + 3][i][j] - min) / range;
				// row[j] = (uchar)(scaled_value * 255);
				_Mat(i, j)[0] = (uchar)(scaled_value * 255);
				_Mat(i, j)[1] = (uchar)(scaled_value * 255);
				_Mat(i, j)[2] = (uchar)(scaled_value * 255);
				_Mat2(i, j)[0] = (uchar)(scaled_value * 255);
				_Mat2(i, j)[1] = (uchar)(scaled_value * 255);
				_Mat2(i, j)[2] = (uchar)(scaled_value * 255);
			}
		}

		// first loop find out the max & min
		double max_vertical = std::numeric_limits<double>::min();
		double min_vertical = std::numeric_limits<double>::max();
		double max_horizonal = std::numeric_limits<double>::min();
		double min_horizonal = std::numeric_limits<double>::max();
		for (int i = 0; i < vertical_features[s].size(); ++i) {
			max_vertical = max_vertical < vertical_features[s][i] ? vertical_features[s][i] : max_vertical;
			min_vertical = min_vertical > vertical_features[s][i] ? vertical_features[s][i] : min_vertical;
		}
		for (int i = 0; i < horizonal_features[s].size(); ++i) {
			max_horizonal = max_horizonal < horizonal_features[s][i] ? horizonal_features[s][i] : max_horizonal;
			min_horizonal = min_horizonal > horizonal_features[s][i] ? horizonal_features[s][i] : min_horizonal;
		}

		// second loop
		cv::Point** points = new cv::Point*[2];
		points[0] = new cv::Point[vertical_features[s].size()];
		points[1] = new cv::Point[horizonal_features[s].size()];
		for (int i = 0; i < vertical_features[s].size(); ++i) {
			double value = (width_ * (vertical_features[s][i] - min_vertical) / (max_vertical - min_vertical));
			points[0][i] = cv::Point(value, i);
		}
		for (int i = 0; i < horizonal_features[s].size(); ++i) {
			double value = (height_ * (horizonal_features[s][i] - min_horizonal) / (max_horizonal - min_horizonal));
			points[1][i] = cv::Point(i, value);
		}
		int thickness = 2;
		int line_type = 8;

		int* npts = new int[2];
		npts[0] = height_;
		npts[1] = width_;

		cv::polylines(mat, (const cv::Point**)&points[0], (const int*)&npts[0], 1, false, cv::Scalar(255,0,0), thickness, line_type); 
		
		cv::polylines(mat2, (const cv::Point**)&points[1], (const int*)&npts[1], 1, false, cv::Scalar(0,255,0), thickness, line_type); 
		

		std::vector<int> vertical_lines, horizontal_lines;

		vertical_lines = FindLoacalMinimalAndMaximal(vertical_features[s]);
		horizontal_lines = FindLoacalMinimalAndMaximal(horizonal_features[s]);

		// action_parameters_vertical_[s].push_back(vertical_lines[0]);
		cv::line(mat, cv::Point(0, 0), cv::Point(width_, 0), 
			cv::Scalar(0, 0, 255), 4, line_type);

		for (int i = 1; i < vertical_lines.size(); ++i) {
			if (vertical_features[s][vertical_lines[i]] < vertical_features[s][vertical_lines[i - 1]]) {
				action_parameters_vertical_[s].push_back(vertical_lines[i] - vertical_lines[i - 1]);

				cv::line(mat, cv::Point(0, vertical_lines[i]), cv::Point(width_, vertical_lines[i]), 
					cv::Scalar(255, 255, 0), 2, line_type);
				cv::line(mat, cv::Point(0, vertical_lines[i - 1]), cv::Point(width_, vertical_lines[i - 1]), 
					cv::Scalar(155, 155, 0), 2, line_type);
			}
		}
		// action_parameters_vertical_[s].push_back(height_ - vertical_lines[vertical_lines.size() - 1]);

		cv::line(mat2, cv::Point(0, 0), cv::Point(0, height_), 
			cv::Scalar(0, 0, 255), 4, line_type);
		// action_parameters_horizontal_[s].push_back(horizontal_lines[s]);
		for (int i = 1; i < horizontal_lines.size(); ++i) {
			if (horizonal_features[s][horizontal_lines[i]] < horizonal_features[s][horizontal_lines[i - 1]]) {
				action_parameters_horizontal_[s].push_back(horizontal_lines[i] - horizontal_lines[i - 1]);

				cv::line(mat2, cv::Point(horizontal_lines[i], 0), cv::Point(horizontal_lines[i], height_), 
					cv::Scalar(255, 255, 0), 2, line_type);
				cv::line(mat2, cv::Point(horizontal_lines[i - 1], 0), cv::Point(horizontal_lines[i - 1], height_), 
					cv::Scalar(155, 155, 0), 2, line_type);
			}
		}
		//action_parameters_horizontal_[s].push_back(width_ - horizontal_lines[horizontal_lines.size() - 1]);
		
		std::ostringstream image_path1, image_path2;
		image_path1 << path << facade_grammar_->get_terminal_symbol(s) << "_vertial.png";
		image_path2 << path << facade_grammar_->get_terminal_symbol(s) << "_horizontal.png";
		imwrite(image_path1.str(), mat);
		imwrite(image_path2.str(), mat2);

		delete[] npts;
		delete[] points[0];
		delete[] points[1];
		delete[] points;
	}
}

void FacadeGeography::ComputeFeature2() {
	int symbols = this->facade_grammar_->get_terminal_symbol_size();

	vertical_features.resize(symbols);
	horizonal_features.resize(symbols);

	for (int d = 0; d < symbols; ++d) {
		// for each symbol
		// vertical feature
		for (int i = 0; i < height_; ++i)
		{
			double sum = 0;
			for (int j = 0; j < width_; ++j)
			{
				if (grd_[2][i][j] == d)
					sum++;
			}
			sum /= width_;
			vertical_features[d].push_back(sum);
		}

		// horizontal feature
		for (int i = 0; i < width_; ++i)
		{
			double sum = 0;
			for (int j = 0; j < height_; ++j)
			{
				if (grd_[2][j][i] == d)
					sum++;
			}
			sum /= height_;
			horizonal_features[d].push_back(sum);
		}
	}
}

/*
void FacadeGeography::ComputeFeature(pcl::PointCloud<pcl::PointXYZ>::Ptr facade_cloud) {
	// First down sample the dense cloud
	std::cout << "down sampling ... \n";
	pcl::VoxelGrid<pcl::PointXYZ> filter;
	filter.setInputCloud(facade_cloud);
	filter.setLeafSize(0.1f, 0.1f, 0.1f);
	pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>);
	filter.filter(*filtered_cloud);
	std::cout << "down sampling done. After filter, " << filtered_cloud->width * filtered_cloud->height <<
		" points left.\n";
	//
	pcl::search::KdTree<pcl::PointXYZ> search;

	pcl::PointXYZ cloud_min, cloud_max;
	pcl::getMinMax3D(*filtered_cloud, cloud_min, cloud_max);

	boost::progress_display  display(height_ + width_);
	Eigen::Vector4f min_pt, max_pt;
	for (int i = 0; i < width_; ++i) // for each width, compute the average z value
	{
		min_pt(0) = extents_min_.x + i * resolution_;
		min_pt(1) = extents_min_.y;
		min_pt(2) = cloud_min.x;
		min_pt(3) = 1.0;

		max_pt(0) = min_pt(0) + resolution_;
		max_pt(1) = min_pt(1) + resolution_ * height_;
		max_pt(2) = cloud_max.x;
		max_pt(3) = 1.0;

		std::vector<int> indices;
		pcl::getPointsInBox(*filtered_cloud, min_pt, max_pt, indices);

		assert(indices.size() >= 0);
		double sum = 0.0;
		for (int k = 0; k < indices.size(); ++k)
		{
			sum += filtered_cloud->points[indices[k]].z;
		}
		sum /= indices.size();

		horizonal_feature.push_back(sum);
		++display;
	}


	for (int i = 0; i < height_; ++i) // for each height, compute the average z value
	{
		min_pt(0) = extents_min_.x;
		min_pt(1) = extents_min_.y + i * resolution_;
		min_pt(2) = cloud_min.x;
		min_pt(3) = 1.0;

		max_pt(0) = min_pt(0) + resolution_ * width_;
		max_pt(1) = min_pt(1) + resolution_;
		max_pt(2) = cloud_max.x;
		max_pt(3) = 1.0;

		std::vector<int> indices;
		pcl::getPointsInBox(*filtered_cloud, min_pt, max_pt, indices);

		assert(indices.size() >= 0);
		double sum = 0.0;
		for (int k = 0; k < indices.size(); ++k)
		{
			sum += filtered_cloud->points[indices[k]].z;
		}
		sum /= indices.size();

		vertical_feature.push_back(sum);
		++display;
	}
}

#define UNIQUE_ELEMENT 0

void FacadeGeography::CalculateActionParameters(double vertical_thread, double horizontal_thread) {
	int last_symbol;

	if (vertical_feature[0] > vertical_thread)
	{
		last_symbol = 0; // wall
	}
	else
	{
		last_symbol = 1; // window
	}

	int parameter = 1;
	for (int i = 1; i < vertical_feature.size(); ++i)
	{
		if (vertical_feature[i] < vertical_thread)
		{ // This position is window
			if (last_symbol == 1) // If last symbol is also window
			{
				parameter++; // add the window size
			}
			else // If last symbol is not window
			{
				//  Record last wall
				if (parameter > 2) // skip the too small parameters, because no so small wall and window.
				{
#if UNIQUE_ELEMENT
					bool already_has = false;
					for (int k = 0; k < vertical_wall_parameters.size(); ++k)
					{
						if (vertical_wall_parameters[k] == parameter)
						{
							already_has = true;
						}
					}
					if (!already_has) // skip the already has parameters.
					{
						vertical_wall_parameters.push_back(parameter);
					}
#else
					vertical_wall_parameters.push_back(parameter);
#endif
					// And reset the current symbol to window 
					parameter = 1;
					last_symbol = 1;
				}
				{
					last_symbol = 1;
				}

			}
		}
		else // This position is wall
		{
			if (last_symbol == 0) // If last symbol is also wall
			{
				parameter++; // add the wall size
			}
			else // If last symbol is not wall
			{
				//  Record last window
				if (parameter > 2) // skip the too small parameters, because no so small wall and window.
				{
#if UNIQUE_ELEMENT
					bool already_has = false;
					for (int k = 0; k < vertical_window_parameters.size(); ++k)
					{
						if (vertical_window_parameters[k] == parameter)
						{
							already_has = true;
						}
					}
					if (!already_has) // skip the already has parameters.
					{
						vertical_window_parameters.push_back(parameter);
					}
#else
					vertical_window_parameters.push_back(parameter);
#endif
					// And reset the current symbol to wall 
					parameter = 1;
					last_symbol = 0;
				}
				else
				{
					last_symbol = 0;
				}

			}
		}

	}

	if (horizonal_feature[0] > horizontal_thread)
	{
		last_symbol = 0; // wall
	}
	else
	{
		last_symbol = 1; // window
	}

	parameter = 1;
	for (int i = 1; i < horizonal_feature.size(); ++i)
	{
		if (horizonal_feature[i] < horizontal_thread)
		{ // This position is window
			if (last_symbol == 1) // If last symbol is also window
			{
				parameter++; // add the window size
			}
			else // If last symbol is not window
			{
				//  Record last wall
				if (parameter > 2) // skip the too small parameters, because no so small wall and window.
				{
#if UNIQUE_ELEMENT
					bool already_has = false;
					for (int k = 0; k < horizontal_wall_parameters.size(); ++k)
					{
						if (horizontal_wall_parameters[k] == parameter)
						{
							already_has = true;
						}
					}
					if (!already_has) // skip the already has parameters.
					{
						horizontal_wall_parameters.push_back(parameter);
					}
#else
					horizontal_wall_parameters.push_back(parameter);
#endif
					// And reset the current symbol to window 
					parameter = 1;
					last_symbol = 1;
				}
				else
				{
					last_symbol = 1;
				}

			}
		}
		else // This position is wall
		{
			if (last_symbol == 0) // If last symbol is also wall
			{
				parameter++; // add the wall size
			}
			else // If last symbol is not wall
			{
				//  Record last window
				if (parameter > 2) // skip the too small parameters, because no so small wall and window.
				{
#if UNIQUE_ELEMENT
					bool already_has = false;
					for (int k = 0; k < horizontal_window_parameters.size(); ++k)
					{
						if (horizontal_window_parameters[k] == parameter)
						{
							already_has = true;
						}
					}
					if (!already_has) // skip the already has parameters.
					{
						horizontal_window_parameters.push_back(parameter);
					}
#else
					horizontal_window_parameters.push_back(parameter);
#endif
					// And reset the current symbol to wall 
					parameter = 1;
					last_symbol = 0;
				}
				else
				{
					last_symbol = 0;
				}	
			}
		}
	}
}

*/
#if 0
void FacadeGeography::CalculateActionParameters2(double vertical_thread, double horizontal_thread) {
	std::string last_symbol;
	int vertical_min_size = 2;
	int horizontal_min_size = 2;

	if (vertical_feature[0] < vertical_thread)
	{
		last_symbol = "wall"; // wall
	}
	else
	{
		last_symbol = "window"; // window
	}

	int parameter = 1;
	for (int i = 1; i < vertical_feature.size(); ++i)
	{
		if (vertical_feature[i] < vertical_thread)
		{ // This position is wall
			if (last_symbol == "wall") // If last symbol is also wall
			{
				parameter++; // add the wall size
				if (i == (vertical_feature.size() - 1))
				{
					action_parameters_vertical_.push_back(std::make_pair(last_symbol, parameter));
				}
			}
			else // If last symbol is not wall
			{
				//  Record last window
				if (parameter > vertical_min_size) // skip the too small parameters, because no so small wall and window.
				{
#if UNIQUE_ELEMENT
					bool already_has = false;
					for (int k = 0; k < vertical_wall_parameters.size(); ++k)
					{
						if (vertical_wall_parameters[k] == parameter)
						{
							already_has = true;
						}
					}
					if (!already_has) // skip the already has parameters.
					{
						vertical_wall_parameters.push_back(parameter);
					}
#else
					//vertical_wall_parameters.push_back(parameter);
					action_parameters_vertical_.push_back(std::make_pair(last_symbol, parameter));
#endif
					// And reset the current symbol to window 
					parameter = 1;
					last_symbol = "wall";
				}
				{
					last_symbol = "wall";
				}
			}
		}
		else // This position is window
		{
			if (last_symbol == "window") // If last symbol is also window
			{
				parameter++; // add the window size
				if (i == (vertical_feature.size() - 1))
				{
					action_parameters_vertical_.push_back(std::make_pair(last_symbol, parameter));
				}
			}
			else // If last symbol is not window
			{
				//  Record last wall
				if (parameter > vertical_min_size) // skip the too small parameters, because no so small wall and window.
				{
#if UNIQUE_ELEMENT
					bool already_has = false;
					for (int k = 0; k < vertical_window_parameters.size(); ++k)
					{
						if (vertical_window_parameters[k] == parameter)
						{
							already_has = true;
						}
					}
					if (!already_has) // skip the already has parameters.
					{
						vertical_window_parameters.push_back(parameter);
					}
#else
					//vertical_window_parameters.push_back(parameter);
					action_parameters_vertical_.push_back(std::make_pair(last_symbol, parameter));
#endif
					// And reset the current symbol to window 
					parameter = 1;
					last_symbol = "window";
				}
				else
				{
					last_symbol = "window";
				}

			}
		}

	}

	if (horizonal_feature[0] < horizontal_thread)
	{
		last_symbol = "wall"; // wall
	}
	else
	{
		last_symbol = "window"; // window
	}

	parameter = 1;
	for (int i = 1; i < horizonal_feature.size(); ++i)
	{
		if (horizonal_feature[i] < horizontal_thread)
		{ // This position is wall
			if (last_symbol == "wall") // If last symbol is also wall
			{
				parameter++; // add the wall size

				if (i == (horizonal_feature.size() - 1))
				{
					action_parameters_horizontal_.push_back(std::make_pair(last_symbol, parameter));
				}
			}
			else // If last symbol is not wall
			{
				//  Record last window
				if (parameter > horizontal_min_size) // skip the too small parameters, because no so small wall and window.
				{
#if UNIQUE_ELEMENT
					bool already_has = false;
					for (int k = 0; k < horizontal_wall_parameters.size(); ++k)
					{
						if (horizontal_wall_parameters[k] == parameter)
						{
							already_has = true;
						}
					}
					if (!already_has) // skip the already has parameters.
					{
						horizontal_wall_parameters.push_back(parameter);
					}
#else
					action_parameters_horizontal_.push_back(std::make_pair(last_symbol, parameter));
					//horizontal_wall_parameters.push_back(parameter);
#endif
					// And reset the current symbol to wall 
					parameter = 1;
					last_symbol = "wall";
				}
				else
				{
					last_symbol = "wall";
				}

			}
		}
		else // This position is window
		{
			if (last_symbol == "window") // If last symbol is also window
			{
				parameter++; // add the window size

				if (i == (horizonal_feature.size() - 1))
				{
					action_parameters_horizontal_.push_back(std::make_pair(last_symbol, parameter));
				}
			}
			else // If last symbol is not window
			{
				//  Record last wall
				if (parameter > horizontal_min_size) // skip the too small parameters, because no so small wall and window.
				{
#if UNIQUE_ELEMENT
					bool already_has = false;
					for (int k = 0; k < horizontal_window_parameters.size(); ++k)
					{
						if (horizontal_window_parameters[k] == parameter)
						{
							already_has = true;
						}
					}
					if (!already_has) // skip the already has parameters.
					{
						horizontal_window_parameters.push_back(parameter);
					}
#else
					action_parameters_horizontal_.push_back(std::make_pair(last_symbol, parameter));
					//horizontal_window_parameters.push_back(parameter);
#endif
					// And reset the current symbol to window 
					parameter = 1;
					last_symbol = "window";
				}
				else
				{
					last_symbol = "window";
				}	
			}
		}
	}
}
#endif

void FacadeGeography::SaveActionParammeters(std::string parater_path) {
	std::ofstream fout(parater_path.c_str());
	/*
	fout << "vertical" << std::endl;
	for (int i = 0; i < height_; ++i)
	{
	fout << vertical_feature[i] << " ";
	}
	fout << std::endl << "horizontal" << std::endl;
	for (int i = 0; i < width_; ++i)
	{
	fout << horizonal_feature[i] << " ";
	}
	fout << std::endl;
	*/
	//fout << "vertical__wall_parameters " << " " << vertical_wall_parameters.size() << std::endl;
	//for (int i = 0; i < vertical_wall_parameters.size(); ++i)
	//{
	//	fout << vertical_wall_parameters[i] << " ";
	//}
	//fout << std::endl;

	//fout << "vertical__window_parameters " << " " << vertical_window_parameters.size() << std::endl;
	//for (int i = 0; i < vertical_window_parameters.size(); ++i)
	//{
	//	fout << vertical_window_parameters[i] << " ";
	//}
	//fout << std::endl;

	//fout << "horizontal__wall_parameters " << " " << horizontal_wall_parameters.size() << std::endl;
	//for (int i = 0; i < horizontal_wall_parameters.size(); ++i)
	//{
	//	fout << horizontal_wall_parameters[i] << " ";
	//}
	//fout << std::endl;

	//fout << "horizontal__window_parameters " << " " << horizontal_window_parameters.size() << std::endl;
	//for (int i = 0; i < horizontal_window_parameters.size(); ++i)
	//{
	//	fout << horizontal_window_parameters[i] << " ";
	//}
	//fout << std::endl;

	//fout.close();
}

void FacadeGeography::SaveActionParammeters2(std::string parater_path) {
	std::ofstream fout(parater_path.c_str());
	const int extend = 10;
	const int step = 2;

	fout << "vertical_parameters " << " " << action_parameters_vertical_.size() << std::endl;
	for (int i = 0; i < action_parameters_vertical_.size(); ++i)
	{
		fout << "symbol" << i << "\t" << action_parameters_vertical_[i].size() << std::endl;
		for (int j = 0; j < action_parameters_vertical_[i].size(); ++j) {
			/*for (int k = action_parameters_vertical_[i][j] / 2; k < action_parameters_vertical_[i][j]; k += step)
			fout << k << "\t";*/
			fout << action_parameters_vertical_[i][j] << "\t";
		}
		fout << endl;
	}
	fout << std::endl;

	fout << "horizontal_parameters " << " " << action_parameters_horizontal_.size() << std::endl;
	for (int i = 0; i < action_parameters_horizontal_.size(); ++i)
	{
		fout << "symbol"  << i << "\t" << action_parameters_horizontal_[i].size() << endl;
		for (int j = 0; j < action_parameters_horizontal_[i].size(); ++j) {
			/*for (int k = action_parameters_horizontal_[i][j] / 2; k < action_parameters_horizontal_[i][j]; k += step)
			fout << k << "\t";*/
			fout << action_parameters_horizontal_[i][j] << "\t";
		}
		fout << endl;
	}

	fout.close();
}

void FacadeGeography::ParseActionParameters() {
	int symbols = this->facade_grammar_->get_terminal_symbol_size();

	action_parameters_vertical_.resize(symbols);
	action_parameters_horizontal_.resize(symbols);

	for (int i = 0; i < symbols; ++i) {
		// vertical case
		{
			// first pass, find out threshold value
			double max_value = -1;
			double min_value = 10;
			for (int j = 0; j < vertical_features[i].size(); ++j) {
				if (vertical_features[i][j] > max_value)
					max_value = vertical_features[i][j];
				if (vertical_features[i][j] < min_value)
					min_value = vertical_features[i][j];
			}
			double threshold_value = (max_value + min_value) / 2.0;

			int symbol_start = -1;
			int last_index = -1;
			for (int j = 0; j < vertical_features[i].size(); ++j) {
				if (symbol_start == -1 && vertical_features[i][j] >= threshold_value) {
					symbol_start = 0;
					last_index = j;
					continue;
				}
				if (symbol_start != -1 && vertical_features[i][j] < threshold_value) {
					action_parameters_vertical_[i].push_back(j - last_index);	
					last_index = j;
					symbol_start = -1;
					continue;
				}
				if (symbol_start != -1 && j == vertical_features[i].size() - 1) {
					action_parameters_vertical_[i].push_back(j - last_index);
					last_index = j;
					symbol_start = -1;
					continue;
				}
			}
		}

		// horizontal case
		{
			// first pass, find out threshold value
			double max_value = -1;
			double min_value = 10;
			for (int j = 0; j < horizonal_features[i].size(); ++j) {
				if (horizonal_features[i][j] > max_value)
					max_value = horizonal_features[i][j];
				if (horizonal_features[i][j] < min_value)
					min_value = horizonal_features[i][j];
			}
			double threshold_value = (max_value + min_value) / 2.0;

			int symbol_start = -1;
			int last_index = -1;
			for (int j = 0; j < horizonal_features[i].size(); ++j) {
				if (symbol_start == -1 && horizonal_features[i][j] >= threshold_value) {
					symbol_start = 0;
					last_index = j;
					continue;
				}
				if (symbol_start != -1 && horizonal_features[i][j] < threshold_value) {
					action_parameters_horizontal_[i].push_back(j - last_index);	
					last_index = j;
					symbol_start = -1;
					continue;
				}
				if (symbol_start != -1 && j == horizonal_features[i].size() - 1) {
					action_parameters_horizontal_[i].push_back(j - last_index);
					last_index = j;
					symbol_start = -1;
					continue;
				}
			}
		}
	}
	/*for (int i = 0; i < action_parameters_vertical_.size(); ++i)
	{
		if (action_parameters_vertical_[i].first == "wall")
		{
			bool already_has = false;
			for (int j = 0; j < vertical_wall_parameters.size(); ++j)
			{
				if (vertical_wall_parameters[j] == action_parameters_vertical_[i].second)
				{
					already_has = true;
				}
			}

			if (!already_has)
			{
				vertical_wall_parameters.push_back(action_parameters_vertical_[i].second);
			}
		}
		else
		{
			bool already_has = false;
			for (int j = 0; j < vertical_window_parameters.size(); ++j)
			{
				if (vertical_window_parameters[j] == action_parameters_vertical_[i].second)
				{
					already_has = true;
				}
			}

			if (!already_has)
			{
				vertical_window_parameters.push_back(action_parameters_vertical_[i].second);
			}
		}
	}

	for (int i = 0; i < action_parameters_horizontal_.size(); ++i)
	{
		if (action_parameters_horizontal_[i].first == "wall")
		{
			bool already_has = false;
			for (int j = 0; j < horizontal_wall_parameters.size(); ++j)
			{
				if (horizontal_wall_parameters[j] == action_parameters_horizontal_[i].second)
				{
					already_has = true;
				}
			}

			if (!already_has)
			{
				horizontal_wall_parameters.push_back(action_parameters_horizontal_[i].second);
			}
		}
		else
		{
			bool already_has = false;
			for (int j = 0; j < horizontal_window_parameters.size(); ++j)
			{
				if (horizontal_window_parameters[j] == action_parameters_horizontal_[i].second)
				{
					already_has = true;
				}
			}

			if (!already_has)
			{
				horizontal_window_parameters.push_back(action_parameters_horizontal_[i].second);
			}
		}

	}*/
}

